/*
 * Copyright (c) 2010 Mathew Hall, University of Sheffield.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *
 * Neither the name of the University of Sheffield nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package search.genes;

import static gpinterpreter.vector.VecSymbol.MOVE;
import static gpinterpreter.vector.VecSymbol.NOP;
import gpinterpreter.GPParser;
import gpinterpreter.vector.VecInstruction;
import gpinterpreter.vector.VecSymbol;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jgap.BaseGene;
import org.jgap.Configuration;
import org.jgap.Gene;
import org.jgap.InvalidConfigurationException;
import org.jgap.RandomGenerator;
import org.jgap.UnsupportedRepresentationException;

public class SymbolInstructionGene extends BaseGene implements InstructionGene {

    public int graphSize;
    private static VecSymbol[] symbols = {MOVE, /* SPLIT, */ NOP // ,MERGE
    };

    // TODO: profile this with and without SPLIT - why is split so cheap?
    public void setGraphSize(int s) {
        graphSize = s;
    }

    public int getGraphSize() {
        return graphSize;
    }

    public InstructionGene clone() {
        SymbolInstructionGene sg;
        try {
            sg = new SymbolInstructionGene(this.getConfiguration(), inst.clone(), graphSize);
        } catch (InvalidConfigurationException ex) {
            Logger.getLogger(SymbolInstructionGene.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException("Attempt to clone SymbolInstructionGene failed");
        }
        return sg;
    }
    private static final long serialVersionUID = 1L;
    private VecInstruction inst;

    public SymbolInstructionGene() throws InvalidConfigurationException {
        this(null);

    }

    public SymbolInstructionGene(Configuration aConfiguration)
            throws InvalidConfigurationException {
        super(aConfiguration);
        inst = new VecInstruction(NOP);

    }

    public SymbolInstructionGene(Configuration aConfiguration, int gs)
            throws InvalidConfigurationException {
        super(aConfiguration);
        inst = new VecInstruction(NOP);
        graphSize = gs;

    }

    public SymbolInstructionGene(Configuration aConfiguration, VecInstruction i)
            throws InvalidConfigurationException {
        super(aConfiguration);
        inst = i.clone();
    }

    public SymbolInstructionGene(Configuration aConfiguration, VecInstruction i,
            int gs) throws InvalidConfigurationException {
        super(aConfiguration);
        inst = i.clone();
        graphSize = gs;
    }

    @Override
    protected Object getInternalValue() {
        if (inst == null) {
            return inst;
        }
        return inst.clone();
    }

    @Override
    protected Gene newGeneInternal() {
        try {
            return new SymbolInstructionGene(getConfiguration(), inst,
                    graphSize);
        } catch (InvalidConfigurationException e) {
            throw new IllegalStateException(e.getMessage());

        }
    }

    @Override
    public void applyMutation(int index, double percentage) {

        VecInstruction toMutate = inst;
        double pc = percentage;
        percentage = Math.abs(percentage);

        if (index % 3 == 0) {
            toMutate.setOperand1((int) (Math.abs(toMutate.getOperand1()
                    + (pc * graphSize))));
        } else if (index % 3 == 1) {
            toMutate.setOperand2((int) (Math.abs(toMutate.getOperand2()
                    + (pc * graphSize))));
        } else {
            double places = (symbols.length - 1) * percentage;
            switch (toMutate.getType()) {
                case NOP:
                    places++;
                //case MERGE:
                //    places++;
                //case SPLIT:
                //    places++;
            }
            places = Math.round(places) % (symbols.length);
            toMutate.setType(symbols[(int) places]);

        }


        if (toMutate.getOperand1() == toMutate.getOperand2()) {
            toMutate.setOperand1(toMutate.getOperand1() + 1);
        }



        if (toMutate.getOperand1() <= 0) {
            toMutate.setOperand1(1);
        }
        if (toMutate.getOperand2() <= 0) {
            toMutate.setOperand2(1);
        }


        if (toMutate.getOperand1() > graphSize) {
            toMutate.setOperand1((int) (toMutate.getOperand1() % (double) graphSize));
        }
        if (toMutate.getOperand2() > graphSize) {
            toMutate.setOperand2((int) (toMutate.getOperand2() % (double) graphSize));
        }


    }

    @Override
    public String getPersistentRepresentation()
            throws UnsupportedOperationException {

        return inst.toString();
    }

    @Override
    public void setAllele(Object arg0) {
        inst = ((VecInstruction) arg0).clone();
    }

    @Override
    public void setToRandomValue(RandomGenerator arg0) {
        double randDouble = arg0.nextDouble();
        randDouble = Math.abs(randDouble);

        double places = (symbols.length - 1) * arg0.nextDouble();
        places = Math.abs(Math.round(places) % (symbols.length));
        if (graphSize < 1) {
            Logger.getLogger(SymbolInstructionGene.class.getName()).log(Level.WARNING, "Tried to set a gene to a random value with 0 graph size!");
        }
        inst = new VecInstruction(symbols[(int) places], arg0.nextInt(graphSize) + 1, arg0.nextInt(graphSize) + 1);

        VecInstruction lastInst = inst;
        if (lastInst.getOperand1() <= 0) {
            lastInst.setOperand1(1);
        }
        if (lastInst.getOperand2() <= 0) {
            lastInst.setOperand2(1);
        }
        if (lastInst.getOperand3() <= 0) {
            lastInst.setOperand3(1);
        }

    }

    @Override
    public void setValueFromPersistentRepresentation(String arg0)
            throws UnsupportedOperationException,
            UnsupportedRepresentationException {
        // need to read graphSize here

        try {

            List<VecInstruction> program = GPParser.parse(arg0);
            inst = program.get(0);
        } catch (Exception e) {
            throw new UnsupportedOperationException(
                    "Parse error on persistent representation");
        }

    }

    @Override
    public int compareTo(Object o) {
        if (o == null) {
            return 1;
        }

        if (inst == null) {
            if (((SymbolInstructionGene) o).getAllele() == null) {
                return 0;
            } else {
                return -1;
            }
        }

        VecInstruction p2 = (VecInstruction) ((SymbolInstructionGene) o).getAllele();

        if (p2 != inst) {
            // do something
            if (p2.getType() == NOP && inst.getType() != NOP) {
                return 1;
            }
            if (inst.getType() == NOP && p2.getType() != NOP) {
                return -1;
            }

        }

        return 0;
    }

    public int hashCode() {
        return inst.hashCode();
    }

    @Override
    public VecInstruction getInstruction() {
        return inst;
    }
}
